結論先說:一張圖光有正確的位置還不夠,query 語意、drill-down 連結與 threshold 來源同樣要經得起 review,否則首頁一樣會在事故現場騙人。
承接上文:上篇把 dashboard 拆成 User Experience 到 AI/GPU 六層,訂出「症狀優先、成因往下鑽」的順序,並示範怎麼把六層收斂成 Service Overview、Workflow and Dependencies、Infrastructure and GPU 三頁。這篇接著處理每張圖背後的 PromQL、variable、link 該怎麼寫,並用一次故障演練與反例清單驗收整套設計。
dashboard 壞掉不一定是 Grafana 壞掉。
更常見的是 metric contract 沒有定義好。
假設你需要追蹤 workflow step。
一個較安全的 metric 設計可以是:
ai_workflow_step_duration_seconds_bucket{
workflow_name,
workflow_version,
step,
outcome,
model_route
}
其中每個 label 都需要寫清楚允許值。
workflow_name = policy_qa | support_triage
workflow_version = v1 | v2
step = prompt.build | retrieval.search | model.chat | tool.execute | answer.validate
outcome = ok | error | degraded
model_route = primary | fallback
請不要寫:
workflow_version = git SHA
每次 deployment 都一個新 SHA,長期 cardinality 仍會上升。
若真的需要 deployment 精確識別,放在 trace resource、log 或 deployment annotation 更合適。
每張 PromQL panel 都先走這個順序。
1. 分子和分母是否描述同一批流量?
2. rate window 是否適合目前 dashboard 時間窗?
3. label 是否會把沒有流量的 slice 變成誤導的 0?
4. histogram 是否正確地先依 le 聚合?
5. query 在失去資料時顯示 no data,還是偽裝成健康?
最後一點尤其重要。
no data 不是自動等於 0。
資料管線停了時,把 no data 顯示成全綠,等於監控系統失明卻自稱沒事。
以下查詢看似合理:
sum(rate(ai_requests_total{task_status="completed"}[5m]))
/
sum(rate(ai_requests_total[5m]))
但若 ai_requests_total 同時包含 /healthz、background worker 與 /ask,分母就被無關請求稀釋了。
更清楚的寫法是先限定服務邊界。
sum(rate(ai_requests_total{
route="/ask",
task_status="completed"
}[5m]))
/
sum(rate(ai_requests_total{
route="/ask"
}[5m]))
即使這個 query 正確,它仍只代表你定義的 task_status。
如果 quality evaluator 只做抽樣,不要把結果混進分子後卻忘了在 title 標示抽樣率。
P95 不能對每個 instance 的 P95 做平均。
正確方向是先把 histogram bucket 依 le 聚合,再用 histogram_quantile 算分位數。
histogram_quantile(
0.95,
sum by (le) (
rate(ai_request_duration_seconds_bucket{
route="/ask"
}[5m])
)
)
若要比較 provider,才把 provider 放進聚合維度。
histogram_quantile(
0.95,
sum by (le, provider) (
rate(ai_model_duration_seconds_bucket[5m])
)
)
這張圖回答「哪個 provider 慢」。
它不應同時用來回答「哪個 prompt version 品質差」。
一張圖一個主要問題,規則仍然適用。
variable 最常見的價值,是讓同一張 dashboard 能切換:
environment
service
model_route
workflow_version
它不應是讓使用者自行拼出一份沒有被 review 過的查詢語言。
一個概念性的 variable 清單:
$environment = dev | staging | production
$service = ai-api | retrieval-api
$route = /ask | /summarize
$model_route = primary | fallback
Panel query 可以用它縮小 scope:
sum by (step) (
rate(ai_workflow_steps_total{
environment="$environment",
service="$service",
route="$route"
}[5m])
)
如果你的 Grafana 設定允許多選,還要確認正規表示式與 escaping 的行為。
最保守的第一版,是從單選 variable 開始。
先把 query 的意義做對,再追求操作便利。
以下設計很常見,也很危險。
$request_id
$user_email
$prompt_text
$trace_id
這些值數量太多,還可能含敏感資訊。
它們不是 Grafana variable 的好候選。
要找單一 request,請由 panel link 帶到 trace backend 或 log search。
Grafana 官方 dashboard schema 包含 templating、links 與 annotations 等結構。
這代表 variable、連結與事件不是外掛技巧,而是 dashboard 設計的一部分。
不過 schema 可用,不表示每個能力都該開。
你仍要為每個 variable 寫一個使用者問題。
Grafana variable 加起來很方便,容易讓人養成「乾脆全部都做成 variable」的習慣——反正切換一下下拉選單就好,何必寫死。但每加一個 variable,都會在使用者心裡多開一道選擇題,而選擇題越多,操作 dashboard 需要的先備知識就越重。四個 variable(environment、service、route、model_route)互相組合,理論上已經有數十種畫面狀態,使用者要先確定自己選對了每一個,才能確定畫面上的線真的代表他想看的東西。如果某個 variable 忘了選、還停在預設值,看到的圖表可能完全對不上他以為在看的範圍,卻不會有任何提示。
這和 ③ 提到的「GPU utilization 高不等於健康」是同一種陷阱的不同版本:表面上看起來多做了一件事(更彈性的篩選),實際上卻在使用者的認知負擔帳戶裡默默扣款。一個實用的檢查方法,是為每個候選 variable 問一句:「如果這個 variable 不存在,使用者會被迫多做幾次手動篩選,還是根本看不到這個切面?」如果答案是「他本來就只會用單一固定值」,那就不必開放成 variable,直接寫死在 query 裡更安全——少一個變因,就少一種故障現場選錯範圍的機會。
一個 drill-down link 應該保留三種 context:
time range
environment
service or route
例如首頁的 /ask latency panel,點擊後應進到 workflow dashboard 的相同時間區間。
概念 URL 如下:
/d/ai-workflow/ai-workflow?
var-environment=${environment}&
var-service=ai-api&
var-route=/ask&
from=${__from}&to=${__to}
實際 dashboard UID、variable 名稱與 URL encoding 必須依你的 Grafana 設定調整。
不要直接把這條示意字串當成 production 設定。
以下 link 標題太抽象:
Details
More
Open
改成:
查看 /ask workflow 分解
查詢同一時間範圍的 error traces
開啟 provider timeout runbook
人不應先點進去才知道目的地。
從 metrics dashboard 連到 trace search 時,只傳可安全篩選的低敏感 context。
service.name
deployment.environment
route
workflow.name
model.route
不要把完整 prompt、回答、使用者輸入或文件內容塞進 URL。
原因不只隱私。
URL 會進入瀏覽器歷史、proxy log、分享截圖與 ticket。
這一點在 dashboard 情境下特別容易被忽略,因為 URL 參數感覺像是「內部傳輸細節」。但事故現場的實際路徑通常是:值班工程師點開 panel link → 截圖貼到 incident channel → 別人把連結轉貼到 postmortem 或 ticket。只要 prompt 曾出現在 URL query string 裡,就會跟著連結一起被轉貼、被內部 wiki 索引,變成一份很難徹底清除的紀錄。相較之下,若 drill-down link 只傳 service.name、route、workflow.name 這類低敏感 context,即使被轉貼一百次,洩漏的頂多是「哪個服務出了問題」。這也是為什麼 ⑨ 範圍那一節要把 prompt、user_id 排除在 Prometheus label 之外——同一個安全邊界,要在 metrics label、panel link、annotation 三處一致地守住,任何一處放行,都等於幫敏感資料開了一條外流的路。
metric 每五分鐘彙整一次時,直接帶完全相同的開始與結束時間,有時會剛好切掉前後文。
可以在 log link 增加小範圍緩衝。
panel window: 10:00–10:05
log search: 09:58–10:07
緩衝不是無限放大。
範圍過大又會讓查詢成本和噪音一起上升。
當你發現 P95 latency 在 14:07 上升,第一個問題通常是:
14:07 前後有什麼變更?
因此 annotation 至少可以標出:
application deployment
prompt version promotion
model route change
retrieval index publish
feature flag enable or disable
incident start and resolved time
每個 annotation 都最好包含:
時間
變更類型
可追查識別值
發起人或系統
rollback 或變更單連結
範例 event:
{
"time": "2026-09-24T14:07:00+08:00",
"event_type": "prompt_release",
"workflow_name": "policy_qa",
"prompt_version": "policy-qa-v4",
"change_id": "CHG-2026-0924-17",
"actor": "release-pipeline"
}
不要把 API key、完整 prompt 或 private ticket 內容寫進 annotation。
annotation 的目標是找到證據,不是把所有變更內容複製一份。
若 dashboard 上只有「v4 發布」這條事件,事故時仍不知道能否回退。
比較好的 annotation 或 link 會指出:
current = policy-qa-v4
previous = policy-qa-v3
rollback path = prompt registry / change CHG-2026-0924-17
這不代表 on-call 可以隨意 rollback。
權限、變更流程與風險仍應由 runbook 定義。
但 dashboard 至少要讓人知道「上一個可比較版本是誰」。
annotation 也有它自己的殭屍化風險,只是形式跟 ⑯ 反例一的四十八個 panel 不太一樣:如果任何一次 feature flag toggle、任何一筆設定變更都自動打上一條 annotation,時間軸很快就會被幾十條垂直線塞滿,反而讓人看不出哪一條和目前的異常真正相關——這和「首頁塞滿成因層指標」是同一種稀釋效應,只是發生在時間軸上而不是 panel 排版上。實務上通常只值得標記那些會實際改變 workflow 行為的事件:deployment、prompt 版本、model route、retrieval index、關鍵 feature flag,而不是每一筆設定檔的微調都自動上牆。判斷標準可以借用 ④ DIY 的同一句話——這個 annotation 如果出現在事故現場,能不能幫值班工程師回答「下一步該做什麼」;答不出來,就不值得佔一條垂直線。
把下面情境當成讀者自行進行的桌上演練。
本文沒有啟動服務,也沒有產生任何真實 metrics。
14:00 新 prompt version 開始接收 20% 流量
14:07 /ask P95 latency 從 1.2s 上升到 4.8s
14:08 good outcome ratio 從 0.98 降到 0.91
14:10 model fallback ratio 上升
14:12 告警觸發
第一個錯誤反應,是打開 Kubernetes 或 GPU dashboard,開始看 CPU。
那是在找成因,不是在確認症狀。
這個反應之所以誘人,是因為盯著 CPU、記憶體圖表捲動,感覺比先花十秒看首頁三個 panel 更像在認真排查。但如果根因跟資源無關(劇本後面會顯示答案是 provider 端出問題),花在 CPU dashboard 上的每一分鐘都是在錯的樹上找答案,故障持續的每一分鐘也都在燒 error budget。這正是 ① 想解決的問題:不是找不到資料,是沒有固定順序,導致大家各憑直覺選自己熟悉的那張圖開始查。
先看三個 panel。
good outcome ratio
/ask P95 latency
request rate
若 request rate 沒有異常暴增,latency 與 outcome 同時惡化,就可確認這不是單純流量成長造成的圖形變化。
此時要做的不是立刻宣布 prompt 是根因。
而是記錄它是接下來要驗證的候選因素。
這一步刻意只花很短時間,因為它的目的是「排除」,不是「解釋」:request rate 正常,排除「只是流量長大」;latency 和 outcome 同時惡化,排除「只是偶發抖動」。排除兩個簡單假說後,才有理由往下一層投入更多時間——首頁三個 panel 的工作是快速篩掉容易誤判的情況,不是找答案。
首頁顯示 policy-qa-v4 在 14:00 發布。
症狀在 14:07 才出現。
這讓它值得調查。
但中間七分鐘的落差,也可能意味著 traffic ramp、cache 狀態或 provider 狀態改變。
你需要更多證據。
annotation 在這裡扮演的角色,是把「值得懷疑的候選」和「無關的巧合」分開,不是把「時間點接近」直接升格成「因果關係」。七分鐘不是隨便給的數字:如果症狀在發布後零點幾秒就出現,時間相關性會強得多;七分鐘的落差,代表中間可能有 ramp-up 曲線、cache warm-up 在起作用。這也呼應 ⑭ 反覆強調的——annotation 只負責告訴你「這裡有一件事發生過」,剩下的因果關係要靠接下來幾個 Step 的證據去補。
在同一個時間窗,依序看:
retrieval.search P95
model.chat P95
provider retry ratio
fallback ratio
answer.validate failure ratio
假設只有 model.chat P95 升高,且 fallback ratio 同時上升。
這時請暫時排除「retrieval 本身變慢」這個假說。
不是因為它永遠不可能慢。
而是目前曲線沒有支持它。
這一步正是 ③ 強調「API 層要拆到子步驟」發揮作用的地方:若 workflow dashboard 只顯示整體 RED,五個候選原因全部平權,只能一個一個排查;拆開後只有一條線真的動了,排查空間立刻從「五選一都要查」縮小成「先查 model.chat」——這個縮小過程省下的時間,正是六層架構要換給值班工程師的東西。
比較 primary 與 fallback provider。
primary model latency:上升
primary timeout ratio:上升
fallback model latency:正常
這組證據比較符合 primary provider 或 primary route 的問題。
它還不能證明 external provider 是根因。
例如你剛好在 prompt v4 裡加大上下文,可能讓 primary route 的 token 數增加。
留意這一步刻意寫的是「比較符合」而不是「證實」——目前的證據無法區分「v4 讓輸入變長 → primary 處理變慢 → timeout」和「primary provider 剛好自己變慢,跟 v4 無關,只是時間點湊巧重疊」這兩種可能。Dependencies 層的判斷就停在「內部還是外部」這條線;要往下區分,就必須換一種證據,這正是 Step 5 要引入 trace 樣本的原因。
從 error 或 slow trace search 取幾條同時間、同版本的 trace。
看的是可比較欄位:
prompt.version
retrieval.document_count
model.route
model.provider
input token bucket
retry event
fallback_triggered
workflow outcome
如果慢 trace 同時顯示:
prompt.version = policy-qa-v4
retrieval.document_count = 12
input token bucket = large
primary provider timeout
fallback triggered = true
那麼更合理的假說是:v4 讓上下文變大,增加 primary 呼叫時間,並放大 timeout 與 fallback。
注意「更合理」不是「已證實」。
還需要對照 v3、同樣流量條件,以及 provider 狀態才能做根因結論。
可能處置包括:
暫停 prompt v4 ramp
回退到 v3
降低 retrieval top-k
暫時調整 model route
啟用已核准的降級回答
向 provider 開 incident ticket
哪一個可做,取決於 pre-approved runbook。
dashboard 不應替人繞過 change control。
它的工作是讓人用足夠證據做出正確升級與處置決策。
回頭看這六個 Step,每一步證據強度都在往上收斂,但沒有任何一步單獨成立:首頁只排除「流量成長」;annotation 只建立「時間點值得懷疑」;workflow dashboard 只鎖定「哪一段變慢」;dependency dashboard 只區分「primary 還是 fallback」;trace 樣本才把「v4 導致 context 變大」這個假說跟實際請求對上。如果跳過任何一步就直接執行 rollback,決策仍可能是對的,但那是運氣好,不是證據推出來的。六層架構真正要訓練的習慣,是接受每一層只回答自己該回答的問題,把判斷留到證據足夠時才下。
第一列同時放:
CPU
memory
disk IOPS
request rate
P50
P95
P99
5xx
queue depth
GPU utilization
token cost
cache hit ratio
問題不是任何指標不重要。
問題是它們沒有優先順序。
修正方式是把第一頁收斂為使用者影響與決策。
其餘指標透過 link 和第二層 dashboard 取得。
這種首頁通常不是一次設計出來的,而是每次有人抱怨「上次那個問題怎麼沒監控到」,就往第一列再加一個 panel,長年累月疊出四十八格。這個累積機制本身就值得注意:它把「補上一個新指標」錯認成「補上一次修復」,兩者感覺很像,實際上完全不同——多加一個 panel 不會讓系統更可靠,只是讓下一個值班工程師要多花一秒鐘決定要不要看這格。真正該做的修復,往往是回頭檢查上一次那個沒被監控到的問題,能不能被歸進現有六層裡的某一層,而不是無止盡地在首頁旁邊加新格子。
有人把 P95 變紅設成 2s,因為「兩秒好像很慢」。
但若產品承諾是 5 秒內回覆,這個紅色會帶來噪音。
反過來說,若使用者需要即時互動,兩秒可能已經太晚。
門檻要連回 SLO、使用者旅程或 baseline。
沒有來源的門檻不是保守。
它只是在把個人直覺編譯成告警噪音。
平均 latency 800ms 看似健康。
但 P99 已經 18 秒,恰好是付費客戶或長問題都被卡住。
平均值可以留在次要 panel。
第一層應優先顯示和 SLO 對應的分位數。
這不是假設性的風險。Uber 的 metrics 平台 M3 曾在一次例行依賴升級後,Prometheus 相容端點的寫入延遲從平均個位數毫秒暴衝到 P99 超過 20 秒,根本原因是新版依賴讓 Go runtime 在高併發下出現鎖競爭。麻煩的是,事故發生當下 request rate 沒有異常、error rate 也沒有升高——請求量與失敗數這兩條線完全正常,因為系統仍然「有在服務」,只是每個請求都被鎖等待拖慢。如果 dashboard 只看平均延遲,數字很可能還停留在個位數毫秒,因為多數請求仍然很快,只有落在尾端的少數請求被鎖等待拖到 20 秒;只有把 duration 拆成分位數、盯著 P99,才會看到那條線已經衝出天際。這個案例把「RED 的 Duration 要用分位數,不能用平均」這件事,從一句原則變成一次真實發生過、而且差點被平均值蓋過去的事故。
這會讓每一筆請求生成新 time series。
短期內看似很容易查單筆 request。
長期則會讓 metrics backend 因 cardinality 付出代價。
修正方法不是刪掉 correlation。
而是把 request_id 留在 log 與 trace,並用 link 從聚合訊號鑽進去。
一張綠色圖代表觀測到的資料在某個時間窗沒有跨門檻。
它不自動代表你的服務已經符合合約 SLA。
中間仍可能有:
資料缺口
錯誤分母
抽樣偏差
時鐘不一致
監控管線中斷
SLO 報告必須把計算定義和資料限制寫清楚。
incident dashboard 可以存在。
但它應有到期日。
事件結束後,將仍有長期價值的 panel 合併回既有 dashboard。
其餘就封存或刪除。
否則「為了排查而暫建」會慢慢成為沒人敢碰的 dashboard 墳場。
一個可以直接抄的到期日規則,是「連續 60 天沒有被開啟過的 dashboard,先標記待確認,再過一段緩衝期沒人異議就封存」。這個門檻不是憑感覺訂的——前面提到的 DEV Community 稽核文章,同一篇報告裡也記錄了幾個團隊在採用類似規則、搭配統一用 USE 和 RED 這種固定框架而不是每張圖自訂結構之後,Datadog 上的自訂 metric 數量下降了一到三成。這個數字本身不用照抄,重點是它證明「刪 dashboard」不是一次性的大掃除,而是可以寫成規則、定期自動跑的日常維運動作——跟程式碼倉庫定期清理沒人用的 feature flag,是同一種紀律。
還有一種反模式沒有寫進前面六個反例,但同樣常見:花很多時間把 dashboard 做得賞心悅目——配色講究、排版對齊、加了漸層與動畫——卻沒有人真的去驗證過每個 panel 背後的 query 語意。畫面精緻本身會製造一種假象,讓評審者、主管,甚至值班工程師自己,誤以為這份監控已經是「成熟可信」的東西,因而降低了對它進行 query review 的警覺心。⑪ 提過的分母陷阱、聚合陷阱,都不會因為 panel 配色漂亮就自動消失;一張畫得很美但分母算錯的 good outcome ratio 圖,造成的傷害不會比一張陽春的折線圖小,甚至因為看起來更可信,反而更容易被誤判為「已驗證過」而長期沒人回頭檢查。視覺品質和資料品質是兩件事,dashboard review 永遠要先問後者,畫面好看只能是最後才錦上添花的部分。
這一節提供讀者自行完成的步驟。
本文沒有建立 Grafana dashboard、沒有連 Prometheus,也沒有執行 query。
你需要自己的實驗環境中已有:
Grafana
Prometheus 或相容 metrics backend
Day 22 定義的低基數 metrics
Day 23 的 trace backend 或 trace link 目的地
若尚未具備這些條件,不要先複製一堆 dashboard JSON。
先回 Day 22 和 Day 23 補齊 metric 與 trace contract。
這個順序不能倒過來,是因為 dashboard 只是把已經存在的 metric 和 trace 語意畫出來,它自己不創造語意。如果 Day 22 的 metric contract 還沒定義清楚 good outcome 該包含哪些條件,或者 Day 23 的 span 還沒拆到 prompt.build、retrieval.search 這種顆粒度,這裡不管拉出多漂亮的 Grafana panel,畫面上呈現的都會是模糊甚至錯誤的語意,只是被一層好看的視覺包裝蓋住而已。
在自己的工作目錄建立一份規格文件。
# Dashboard inventory
| Dashboard | Primary reader | First decision | Owner |
| :-- | :-- | :-- | :-- |
| Service Overview | on-call | 是否有使用者影響 | ai-platform |
| Workflow | service engineer | 哪個 step 異常 | ai-platform |
| Infrastructure | platform SRE | 是否資源限制 | platform-sre |
先讓每張 dashboard 有一行 contract。
不要先拉 panel。
每個 panel 用同一份模板。
## Panel: /ask P95 latency
Question
使用者是否在 SLO 的延遲門檻外等待?
Metric
ai_request_duration_seconds_bucket
Query scope
route=/ask;environment 由 variable 決定。
Healthy means
P95 未跨 SLO 門檻,且資料來源存在。
Drill-down
Workflow dashboard 的 model.chat 與 retrieval.search 延遲。
Owner
ai-platform。
填不出 Healthy means,表示你還沒有 SLO 或 baseline。
填不出 Drill-down,表示這張圖還沒準備好放到首頁。
這個 panel 模板本質上是 ⑧ dashboard contract 的縮小版,只是把單位從「整張 dashboard」換成「單一 panel」。兩者刻意用同一種結構,是因為 dashboard 的完整性,最終要靠每一個 panel 都經得起同樣的檢查來堆疊出來——contract 只回答「這張圖整體服務誰、決策什麼」,panel 模板才真正逼你面對「這條線背後的 query 準不準」這個更瑣碎、卻也更容易出錯的問題。
把 PromQL 放進 Grafana Explore 或你的 metrics UI。
逐一檢查:
選 staging 時是否真的只有 staging?
route=/ask 是否排除了 health check?
無流量時顯示的是 no data 還是 0?
切不同時間窗時 rate 是否可解讀?
legend 是否能區分步驟而不爆炸?
這一步是 query review,不是 dashboard 美化。
若資料本身不對,調 panel 顏色不會修好它。
為首頁每個主要 panel 加上一條有意義的 link。
good outcome ratio → /ask workflow 分解
model.chat latency → slow traces
provider timeout ratio → dependency runbook
queue depth → worker capacity dashboard
點擊後核對:
environment 是否保留?
route 是否保留?
時間窗是否保留?
連結文字是否說明目的?
這四個核對項目對應的正是 ⑬ 提出的三種 context(time range、environment、service or route)加上連結文字本身。實務上最常漏掉的是時間窗——很多人做 link 時只想著把 environment 和 service 這類 tag 帶過去,卻忘了把 from、to 也用變數帶入,結果點擊後跳到的畫面預設看的是「現在」,而不是首頁當下正在看的那個異常時間點,值班工程師還得手動把時間軸拉回去,白白浪費故障現場最寶貴的幾秒鐘。
選擇一個不能傷害真實服務的桌上情境。
例如:
假設 primary provider 在 15 分鐘內 timeout ratio 上升。
請一位沒有參與 dashboard 設計的同事,從首頁開始。
觀察他是否能在五分鐘內回答:
是否真的有使用者影響?
主要受影響的 route 是哪個?
provider、workflow 還是資源哪一層更可疑?
下一個應打開的頁面或 runbook 是什麼?
如果他只能問「你平常都看哪張」,那 dashboard 的導航仍依賴口耳相傳。
最後把這些問題留在 README 或 dashboard description:
quality SLI 是否抽樣?抽樣率多少?
哪些 label 未被成本壓力測試?
哪幾個 provider 沒有可用的 health metric?
trace link 是否需要權限?
dashboard 是否已由另一位 on-call 實際演練?
這些不是待辦清單的裝飾。
它們是避免你把規劃文件說成已驗證 production runbook 的界線。
寫下「未驗證」不是承認失敗,反而是這份文件最誠實、也最有用的部分。一份宣稱萬事俱備的 dashboard 說明,反而讓人不敢質疑它;一份老實列出邊界的說明,才會有人願意接手繼續補完,也才不會在事故現場因為誤信某個其實沒被驗證過的假設,而做出錯誤的處置。
完成自己的 dashboard 後,用下列清單驗收。
[ ] 首頁能在十秒內回答是否有使用者影響。
[ ] 每個首頁 panel 都有明確問題與 owner。
[ ] SLI 的分子、分母、時間窗寫在文件中。
[ ] 延遲 panel 使用與 SLO 對應的分位數或明確 baseline。
[ ] metrics label 沒有 request_id、prompt、user_id 等高基數值。
[ ] 同一個 dashboard 使用的 variable 有限制範圍與用途說明。
[ ] drill-down link 保留 environment、service 與時間窗。
[ ] trace 和 log link 未在 URL 傳遞敏感 payload。
[ ] deployment、prompt、model 或 index 變更可在時間軸上辨識。
[ ] annotation 被視為相關性線索,不是自動根因。
[ ] no data 的呈現與處置已定義。
[ ] 有人能從首頁走到下一層,而不靠作者口頭指導。
[ ] incident 專用 dashboard 有到期或清理規則。
[ ] 本文中的範例 query 已在讀者自己的資料模型下重新檢查。
前十二項是設計驗收。
最後一項是資料驗收。
兩者都要做,才不是把一張漂亮的空殼交差。
dashboard 的風險不比 application code 小。
錯誤 query 可能隱藏使用者影響。
錯誤 link 可能讓人走到錯的環境。
錯誤 threshold 可能把值班叫醒,直到大家學會忽略它。
因此新增或修改 dashboard 時,review 至少問:
這張圖的讀者與決策是什麼?
query 的分子與分母是否一致?
label 是否會產生 cardinality 風險?
threshold 是否連回 SLO、baseline 或明確理由?
no data 時畫面與告警會怎麼做?
link 是否保留安全的 context?
owner 是否同意維護它?
若 dashboard JSON 使用 Git 管理,還可以加上:
UID 是否穩定?
folder 是否正確?
data source UID 是否可攜?
variable 預設值是否會指向 production?
敏感資料是否被寫進 JSON?
Grafana dashboard API 與 dashboard model 都能表示 folder、panels、annotations、links 與 templating。
這讓 dashboard 可以被版本化。
但版本化不是自動可靠。
仍要由人 review 它回答的問題和資料邊界。
Dashboard 是人主動打開的證據地圖。
Alert 則是系統主動叫人的機制。
兩者若沒有接上,會出現兩種麻煩。
只有 dashboard:事故靠人剛好看到。
只有 alert:人醒來後不知道下一步去哪。
Day 25 會處理 alert 的門檻、訊號品質、行動性與疲勞。
先記住今天的原則:
每一個 alert,都應該有一張能回答「現在發生什麼」的 dashboard。
每一張首頁 dashboard,也應該能指出「何時需要叫人」的 SLO 或 alert。
這個銜接也解釋了為什麼 Day 24 要先於 Day 25:alert 的核心工作是判斷「要不要現在叫醒人」,而這個判斷本身,用的正是今天定義出來的六層架構第一層——User Experience 那條 SLI 曲線和它對應的 error budget burn rate。如果沒有先把「使用者受影響」這件事定義清楚,也沒有先確定分子分母、時間窗、cardinality 這些 ⑪ 講過的細節,Day 25 要設計的 alert 門檻,就等於是蓋在一塊還沒踩實的地基上——alert 觸發了,指向的卻是一個定義模糊、無法拿來立刻判斷嚴重度的 SLI,值班工程師收到通知後第一件事還是得先花時間確認「這個數字到底代表什麼」,而這正是 Day 25 要極力避免的 alert 疲勞成因之一。反過來說,今天把六層 contract 和 drill-down 寫清楚之後,Day 25 的 alert 規則其實只需要做一件新事情:替第一層的 SLI 加上一個明確的觸發門檻與升級路徑,其餘的「看到紅色之後去哪裡查」,六層架構已經先鋪好了。
排對六層順序、替每張圖定好 owner 和下一步,決定的是 on-call 在事故現場多花五分鐘還是五十分鐘。先把 dashboard 當成一份可 review 的決策契約,再把 metrics、trace、logs 與變更事件接成固定鑽取路徑。這樣它才是地圖,不是監控倉庫。
User Experience ← 十秒內回答「要不要處理」
↓
Service Overview ← 判斷是否需要升級
↓
API ← 拆到 span 層級找瓶頸
↓
Dependencies ← 內部還是外部的問題
↓
Infrastructure ← 資源是否撐得住
↓
AI / GPU ← 加速器與推論佇列是否過載
六層順序本身不難記,難的是抵抗「多放一個 panel 總比少放安全」的直覺,以及在服務演化之後仍願意回頭重新走一次 contract 檢查。這兩件事都不是一次做完就結束的工程任務,而是團隊要長期維持的紀律——跟寫測試、做 code review 一樣,價值不在第一次做的時候,而在半年後有人真的需要靠它救火的那一刻。
下一篇會接著處理「什麼才是好的 Alert?」:什麼狀況真的值得叫醒人,以及告警如何指向今天建立的證據地圖。
這篇是 Learning SRE for the AI Era 系列的一部分。
Build → Trace → Break → Measure → Evaluate → Recover → Improve.